package edu.arizona.mindseye;

import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

import com.xuggle.mediatool.IMediaReader;
import com.xuggle.mediatool.MediaToolAdapter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.mediatool.event.IVideoPictureEvent;

import edu.arizona.mindseye.event.Event;
import edu.arizona.mindseye.event.EventListener;
import edu.arizona.mindseye.event.EventManager;
import edu.arizona.mindseye.event.enums.EventType;
import edu.arizona.mindseye.event.enums.Parameter;

public class VideoModel extends MediaToolAdapter {

	private int _currentFrame;
	private List<BufferedImage> _images;
	
	public VideoModel() { 
		super();
		
		_currentFrame = 0;
		_images = new ArrayList<BufferedImage>();
		
		
		EventManager.inst().register(EventType.FRAME_DELTA_COMMAND, new EventListener() {
			@Override
			public void onEvent(Event e) {
				int delta = (Integer) e.getValue(Parameter.delta);
				updateFrame(delta);
			} 
		});
		
		EventManager.inst().register(EventType.SEEK_COMMAND, new EventListener() {
			@Override
			public void onEvent(Event e) {
				int frame = (Integer) e.getValue(Parameter.index);
				setFrame(frame);
			} 
		});
	}
	
	protected void fireModelChanged() { 
		Event e = new Event(EventType.MODEL_CHANGED);
		e.addParameter(Parameter.model, this);
		EventManager.inst().dispatch(e);
	}

	/**
	 * Load the movie
	 * @param fileName
	 */
	public void load(String path, String name) { 
		// we are loading a new movie so reset the current model.
		_images.clear();
		System.gc();
		
		_currentFrame = 0;
		_images = new ArrayList<BufferedImage>();
		
		IMediaReader reader = ToolFactory.makeReader(path + name);
		reader.setBufferedImageTypeToGenerate(BufferedImage.TYPE_3BYTE_BGR);
		reader.addListener(this);

		// This reads in all of the relevant information.
		while (reader.readPacket() == null) { 
			;
		}
		
		// TODO: notify everyone of the image sizes and the number of frames.
		Event e = new Event(EventType.MOVIE_LOADED);
		e.addParameter(Parameter.name, name);
		e.addParameter(Parameter.frames, _images.size());
		e.addParameter(Parameter.width, 0);
		e.addParameter(Parameter.height, 0);
		e.addParameter(Parameter.model, this);
		EventManager.inst().dispatch(e);
		
		fireModelChanged();
	}
	
	@Override
	public void onVideoPicture(IVideoPictureEvent event) { 
		_images.add(event.getImage());
	}
	
	public BufferedImage getImage() { 
		return _images.get(_currentFrame);
	}
	
	public int getIndex() { 
		return _currentFrame;
	}
	
	public void updateFrame(int jump) { 
		_currentFrame += jump;
		constrain();
		fireModelChanged();
	}
	
	public void setFrame(int frame) { 
		_currentFrame = frame;
		constrain();
		fireModelChanged();
	}
	
	/**
	 * Make sure that the current frame is greater than zero and less 
	 * than the total number of frames.
	 */
	private void constrain() { 
		_currentFrame = Math.max(0, _currentFrame);
		_currentFrame = Math.min(_images.size()-1, _currentFrame);
	}
}
